#include "globals.h"
#include "errno.h"

#include "util/string.h"
#include "util/debug.h"

#include "mm/mmobj.h"
#include "mm/pframe.h"
#include "mm/mm.h"
#include "mm/page.h"
#include "mm/slab.h"
#include "mm/tlb.h"

int anon_count = 0; /* for debugging/verification purposes */

static slab_allocator_t *anon_allocator;

static void anon_ref(mmobj_t *o);
static void anon_put(mmobj_t *o);
static int  anon_lookuppage(mmobj_t *o, uint32_t pagenum, int forwrite, pframe_t **pf);
static int  anon_fillpage(mmobj_t *o, pframe_t *pf);
static int  anon_dirtypage(mmobj_t *o, pframe_t *pf);
static int  anon_cleanpage(mmobj_t *o, pframe_t *pf);

static mmobj_ops_t anon_mmobj_ops = {
        .ref = anon_ref,
        .put = anon_put,
        .lookuppage = anon_lookuppage,
        .fillpage  = anon_fillpage,
        .dirtypage = anon_dirtypage,
        .cleanpage = anon_cleanpage
};

/*
 * This function is called at boot time to initialize the
 * anonymous page sub system. Currently it only initializes the
 * anon_allocator object.
 */
void
anon_init()
{
	anon_allocator=slab_allocator_create("anon",sizeof(mmobj_t));
	 KASSERT(anon_allocator);
	dbg(DBG_ELF,"anon_init: anon_allocator is not null\n");		 
 /*       NOT_YET_IMPLEMENTED("VM: anon_init");*/
}

/*
 * You'll want to use the anon_allocator to allocate the mmobj to
 * return, then then initialize it. Take a look in mm/mmobj.h for
 * macros which can be of use here. Make sure your initial
 * reference count is correct.
 */
mmobj_t *
anon_create()
{
	mmobj_t *annonObj=(mmobj_t*)slab_obj_alloc(anon_allocator);
	mmobj_init(annonObj, &anon_mmobj_ops);	
	annonObj->mmo_refcount=1;      
        return annonObj;
/*NOT_YET_IMPLEMENTED("VM: anon_create");*/
}

/* Implementation of mmobj entry points: */

/*
 * Increment the reference count on the object.
 */
static void
anon_ref(mmobj_t *o)
{
	KASSERT(o && (0 <= o->mmo_refcount) && (&anon_mmobj_ops == o->mmo_ops));
	dbg(DBG_ELF,"anon_ref: object pointer not null & object mmo_refcount > 0 & object's mmo_ops is equal to anon_mmobj_ops\n");
	o->mmo_refcount++;
	return;	
    /*    NOT_YET_IMPLEMENTED("VM: anon_ref");*/
}

/*
 * Decrement the reference count on the object. If, however, the
 * reference count on the object reaches the number of resident
 * pages of the object, we can conclude that the object is no
 * longer in use and, since it is an anonymous object, it will
 * never be used again. You should unpin and uncache all of the
 * object's pages and then free the object itself.
 */
static void
anon_put(mmobj_t *o)
{
/*list_iterate_begin(list, var, type, member)*/
KASSERT(o && (0 < o->mmo_refcount) && (&anon_mmobj_ops == o->mmo_ops));
dbg(DBG_ELF,"anon_put: object pointer not null & object mmo_refcount > 0 & object's mmo_ops is not null \n");
	if(o->mmo_refcount < o->mmo_nrespages)
		return;

	o->mmo_refcount--;

	if(o->mmo_refcount==o->mmo_nrespages)
	{
		pframe_t *pf;
		list_link_t *link,*remove_link;
		for(link=o->mmo_respages.l_next;link!=&(o->mmo_respages);)
		{			
			remove_link=link;			
			link=link->l_next;
			pf=list_item(link,pframe_t,pf_olink);
			while(pf->pf_pincount!=0)
				pframe_unpin(pf);								
		}
		
		for(link=o->mmo_respages.l_next;link!=&(o->mmo_respages);)
		{
			remove_link=link;
			link=link->l_next;
			pf=list_item(remove_link,pframe_t,pf_link);
			if(pf->pf_pincount==0 && !pframe_is_free(pf) && !pframe_is_busy(pf))
				pframe_free(pf);
			slab_obj_free(anon_allocator,o);			
		}		
	}

      /*  NOT_YET_IMPLEMENTED("VM: anon_put");*/
}

/* Get the corresponding page from the mmobj. No special handling is
 * required. */
static int
anon_lookuppage(mmobj_t *o, uint32_t pagenum, int forwrite, pframe_t **pf) /* HAVE TO HANDLE FOR WRITE ? */
{
	list_link_t *link;
	pframe_t *pframe;

	for(link=o->mmo_respages.l_next;link!=&(o->mmo_respages);link=link->l_next)
	{
		pframe=list_item(link,pframe_t,pf_olink);
		if(pframe->pf_pagenum==pagenum)
		{
			pf=&pframe;
			return 0;
		}
	}
	
	return -1; 
		
/*        NOT_YET_IMPLEMENTED("VM: anon_lookuppage");*/
}

/* The following three functions should not be difficult. */

static int
anon_fillpage(mmobj_t *o, pframe_t *pf)
{
KASSERT(pframe_is_busy(pf));
dbg(DBG_ELF,"anon_fillpage: pframe is busy\n");
KASSERT(!pframe_is_pinned(pf));
dbg(DBG_ELF,"anon_fillpage: pframe is not pinned\n");

	(void)memset(pf->pf_addr + PAGE_OFFSET(pf->pf_addr),0,PAGE_SIZE-PAGE_OFFSET(pf->pf_addr));
	return 0;			
  /*      NOT_YET_IMPLEMENTED("VM: anon_fillpage");*/
}

static int
anon_dirtypage(mmobj_t *o, pframe_t *pf)
{
	return 0;
       /* NOT_YET_IMPLEMENTED("VM: anon_dirtypage");*/
}

static int
anon_cleanpage(mmobj_t *o, pframe_t *pf)
{
	list_link_t *link;
	pframe_t *pframe;

	for(link=o->mmo_respages.l_next;link!=&(o->mmo_respages);link=link->l_next)
	{
		pframe=list_item(link,pframe_t,pf_olink);
		if(pframe->pf_pagenum==pf->pf_pagenum)
		{
			(void)memcpy(pframe->pf_addr+PAGE_OFFSET(pframe->pf_addr),pf->pf_addr+PAGE_OFFSET(pf->pf_addr),PAGE_SIZE-PAGE_OFFSET(pf->pf_addr));
			return 0;
		}
	}
		
	return -1;
 /*       NOT_YET_IMPLEMENTED("VM: anon_cleanpage");*/
}
